"======================================================================
"
" tools.vim -
"
" Created by skywind on 2019/12/23
" Last Modified: 2021/11/30 14:43
"
"======================================================================

" vim: set noet fenc=utf-8 ff=unix sts=4 sw=4 ts=4 :


"----------------------------------------------------------------------
" list buffer ids
"----------------------------------------------------------------------
function! s:buffer_list()
    let l:ls_cli = get(g:, 'quickui_buffer_list_cli', 'ls t')
    redir => buflist
    silent execute l:ls_cli
    redir END
    let bids = []
    for curline in split(buflist, '\n')
        if curline =~ '^\s*\d\+'
            let bid = str2nr(matchstr(curline, '^\s*\zs\d\+'))
            let bids += [bid]
        endif
    endfor
    return bids
endfunc


"----------------------------------------------------------------------
" get default width
"----------------------------------------------------------------------
function! s:get_tools_width()
    let width = get(g:, 'quickui_tools_width', 70)
endfunc


"----------------------------------------------------------------------
" locals
"----------------------------------------------------------------------
let s:keymaps = '123456789abcdefimnopqrstuvwxyz'


"----------------------------------------------------------------------
" switch buffer callback
"----------------------------------------------------------------------
function! quickui#tools#buffer_switch(bid)
    let switch = get(g:, 'quickui_switch_mode', &switchbuf)
    let code = g:quickui#listbox#current.tag
    let name = fnamemodify(bufname(a:bid), ':p')
    let opts = {}
    let bid = str2nr('' . a:bid)
    if code == ''
        let opts.switch = get(g:, 'quickui_switch_enter', switch)
        call quickui#utils#switch(bid, opts)
    elseif code == '1'
        let opts.switch = get(g:, 'quickui_switch_space', switch)
        call quickui#utils#switch(bid, opts)
    elseif code == '2'
        exec 'b '. a:bid
    elseif code == '3'
        exec 'vs'
        exec 'b '. a:bid
    elseif code == '4'
        exec 'split'
        exec 'b '. a:bid
    elseif code == '5'
        exec 'tab split'
        exec 'b '. a:bid
    elseif code == '6'
        exec 'tab drop ' . fnameescape(name)
    endif
endfunc


"----------------------------------------------------------------------
" get content
"----------------------------------------------------------------------
function! quickui#tools#list_buffer(switch)
    let bids = s:buffer_list()
    let content = []
    let index = 0
    let current = -1
    let bufnr = bufnr()
    let s:switch = a:switch
    for bid in bids
        let key = (index < len(s:keymaps))? strpart(s:keymaps, index, 1) : ''
        let text = '[' . ((key == '')? ' ' : ('&' . key)) . "]\t"
        let text .= "\t"
        let name = fnamemodify(bufname(bid), ':p')
        let main = fnamemodify(name, ':t')
        let path = fnamemodify(name, ':h')
        let buftype = getbufvar(bid, '&buftype')
        if main == ''
            continue
        elseif buftype == 'nofile' || buftype == 'quickfix'
            continue
        endif
        let text = text . main . " " . "(" . bid . ")\t" . path
        let cmd = 'call quickui#tools#buffer_switch(' . bid . ')'
        if a:switch != ''
            " let cmd = a:switch . ' ' . fnameescape(name)
        endif
        let content += [[text, cmd]]
        if bid == bufnr()
            let current = index
        endif
        let index += 1
    endfor
    let opts = {'title': 'Switch Buffer', 'index':current, 'close':'button'}
    let opts.border = g:quickui#style#border
    let opts.keymap = {}
    let opts.keymap["\<space>"] = 'TAG:1'
    let opts.keymap["\<c-e>"] = 'TAG:2'
    let opts.keymap["\<c-]>"] = 'TAG:3'
    let opts.keymap["\<c-x>"] = 'TAG:4'
    let opts.keymap["\<c-t>"] = 'TAG:5'
    let opts.keymap["\<c-g>"] = 'TAG:6'
    if exists('g:quickui_tools_width')
        let opts.w = quickui#utils#tools_width()
    endif
    " let opts.syntax = 'cpp'
    let maxheight = (&lines) * 60 / 100
    if len(content) > maxheight
        let opts.h = maxheight
    endif
    if len(content) == 0
        redraw
        echohl ErrorMsg
        echo "Empty buffer list"
        echohl None
        return -1
    endif
    call quickui#listbox#open(content, opts)
endfunc


"----------------------------------------------------------------------
" list function
"----------------------------------------------------------------------
function! quickui#tools#list_function()
    let ctags = get(g:, 'quickui_ctags_exe', 'ctags')
    if !executable(ctags)
        let msg = 'Not find ctags, add to $PATH or specify in '
        call quickui#utils#errmsg(msg . 'g:quickui_ctags_exe')
        return -1
    endif
    let items = quickui#tags#function_list(bufnr(), &ft)
    if len(items) == 0
        call quickui#utils#errmsg('No content !')
        return -2
    endif
    let content = []
    let cursor = -1
    let index = 0
    let ln = line('.')
    let maxsize = (&columns) * 60 / 100
    let maxheight = (&lines) * 60 / 100
    let maxwidth = 0
    let indents = get(g:, 'quickui_tags_indent', {})
    for item in items
        if ln >= item.line
            let cursor = index
        endif
        let index += 1
        let space = get(indents, item.mode, '')
        let text = '' . item.mode . '' . "   \t" . space . item.text
        let text = text . '  [:' . item.line . ']'
        let maxwidth = (maxwidth < len(text))? len(text) : maxwidth
        let text = substitute(text, '&', '&&', 'g')
        let content += [[text, ':' . item.line]]
    endfor
    let opts = {'title': 'Function List', 'close':'button'}
    if cursor >= 0
        let opts.index = cursor
    endif
    let limit = &columns * 90 / 100
    let opts.h = len(content)
    let opts.h = (opts.h < maxheight)? opts.h : maxheight
    let opts.w = (maxwidth < limit)? maxwidth : limit
    if opts.w < maxsize
        let opts.w = (opts.w < 60)? 60 : opts.w
    endif
    let opts.syntax = 'qui_func'
    if exists('g:quickui_tools_width')
        let opts.w = quickui#utils#tools_width()
    endif
    " let content += ["1\t".repeat('0', 100)]
    call quickui#listbox#open(content, opts)
    return 0
endfunc


"----------------------------------------------------------------------
" preview register in popup and choose to paste
"----------------------------------------------------------------------
function! quickui#tools#list_register()
endfunc


"----------------------------------------------------------------------
" display python help in the textbox
"----------------------------------------------------------------------
function! quickui#tools#python_help(word)
    let python = get(g:, 'quickui_tools_python', '')
    if python == ''
        if executable('python')
            let python = 'python'
        elseif executable('python3')
            let python = 'python3'
        elseif executable('python2')
            let python = 'python2'
        endif
    endif
    if a:word == ''
        let text = getline('.')
        let pre = text[:col('.') - 1]
        let suf = text[col('.'):]
        let word = matchstr(pre, "[A-Za-z0-9_.]*$")
        let word = word . matchstr(suf, "^[A-Za-z0-9_]*")
    else
        let word = a:word
    endif
    let cmd = python . ' -m pydoc ' . shellescape(word)
    let title = 'PyDoc <'. a:word . '>'
    let opts = {'title':title}
    let opts.color = 'QuickBG'
    " let opts.bordercolor = 'QuickBG'
    let opts.tabstop = 12
    call quickui#textbox#command(cmd, opts)
endfunc


"----------------------------------------------------------------------
" display messages
"----------------------------------------------------------------------
function! quickui#tools#display_messages()
    let x = ''
    redir => x
    silent! messages
    redir END
    let x = substitute(x, '[\n\r]\+\%$', '', 'g')
    let content = filter(split(x, "\n"), 'v:key != ""')
    if len(content) == 0
        call quickui#utils#errmsg('Empty messages')
        return -1
    endif
    let opts = {"close":"button", "title":"Vim Messages"}
    if exists('g:quickui_tools_width')
        let opts.w = quickui#utils#tools_width()
    endif
    call quickui#textbox#open(content, opts)
endfunc


"----------------------------------------------------------------------
" preview quickfix
"----------------------------------------------------------------------
function! quickui#tools#preview_quickfix(...)
    if quickui#preview#visible()
        call quickui#preview#close()
        return 0
    endif
    if &bt != 'quickfix'
        call quickui#utils#errmsg('Not in quickfix window !')
        return -1
    endif
    if !exists('b:__quickui_qf__')
        let b:__quickui_qf__ = {}
    endif
    let obj = b:__quickui_qf__
    if !has_key(obj, 'version')
        let obj.version = -1
    endif
    if b:changedtick != obj.version
        if getwininfo(win_getid())[0].loclist != 0
            let obj.items = getloclist(0)
        else
            let obj.items = getqflist()
        endif
        let obj.version = b:changedtick
    endif
    let index = (a:0 > 0)? a:1 : line('.')
    if index < 1 || index > len(obj.items)
        call quickui#utils#errmsg('No information in this line')
        return -2
    endif
    let item = obj.items[index - 1]
    if item.valid == 0
        return -3
    endif
    if item.bufnr <= 0
        return -4
    endif
    let name = bufname(item.bufnr)
    let opts = {'cursor':item.lnum}
    call quickui#preview#open(name, opts)
    " echom 'lnum:'. item.lnum
endfunc


"----------------------------------------------------------------------
" preview tag
"----------------------------------------------------------------------
function! quickui#tools#preview_tag(tagname, ...)
    let tagname = (a:tagname == '')? expand('<cword>') : a:tagname
    if tagname == ''
        call quickui#utils#errmsg('Error: empty tagname')
        return 0
    endif
    let obj = quickui#core#object(0)
    let reuse = 0
    if has_key(obj, 'ptag')
        let ptag = obj.ptag
        if get(ptag, 'tagname', '') == tagname
            let reuse = 1
        endif
    endif
    if reuse == 0
        let obj.ptag = {}
        let ptag = obj.ptag
        let ptag.taglist = quickui#tags#tagfind(tagname)
        let ptag.tagname = tagname
        let ptag.index = 0
    else
        let ptag = obj.ptag
        let ptag.index += 1
        if ptag.index >= len(ptag.taglist)
            let ptag.index = 0
        endif
    endif
    if len(ptag.taglist) == 0
        if a:0 > 0 && a:1
            call quickui#utils#errmsg('E257: preview: tag not find "' . tagname . '"')
        endif
        return 1
    endif
    if ptag.index >= len(ptag.taglist) || ptag.index < 0
        let ptag.index = 0
    endif
    let taginfo = ptag.taglist[ptag.index]
    let filename = taginfo.filename
    if !filereadable(filename)
        call quickui#utils#errmsg('E484: Can not open file '.filename)
        return 2
    endif
    if !has_key(taginfo, 'line')
        call quickui#utils#errmsg('Error: no "line" information in your tags, regenerate with -n')
        return 3
    endif
    let text = '('.(ptag.index + 1).'/'.len(ptag.taglist).')'
    let opts = {'cursor':taginfo.line, 'title':text}
    call quickui#preview#open(filename, opts)
    let text = taginfo.name
    let text.= ' ('.(ptag.index + 1).'/'.len(ptag.taglist).') '
    let text.= filename
    if has_key(taginfo, 'line')
        let text .= ':'.taginfo.line
    endif
    let display = has('gui_running')? 0 : 1
    let display = get(g:, 'quickui_preview_tag_msg', display)
    if display != 0
        call quickui#utils#print(text, 1)
    endif
    return 0
endfunc


"----------------------------------------------------------------------
" display vim help in popup
"----------------------------------------------------------------------
function! quickui#tools#display_help(tag)
    if !exists('s:help_tags')
        let fn = expand('$VIMRUNTIME/doc/tags')
        if filereadable(fn)
            let content = readfile(fn)
            let s:help_tags = {}
            for line in content
                let parts = split(line, "\t")
                if len(parts) >= 3
                    let s:help_tags[parts[0]] = [parts[1], parts[2]]
                endif
            endfor
        endif
    endif
    if !exists('s:help_tags')
        call quickui#utils#errmsg('Sorry, not find help tags in $VIMRUNTIME')
        return -1
    endif
    if !has_key(s:help_tags, a:tag)
        call quickui#utils#errmsg('E149: Sorry, no help for '. a:tag)
        return -2
    endif
    let item = s:help_tags[a:tag]
    let name = expand($VIMRUNTIME . '/doc/' . item[0])
    let command = substitute(item[1], '\*', '', 'g')
    if !filereadable(name)
        call quickui#utils#errmsg('E484: Sorry, cannot open file '.name)
        return -3
    endif
    let content = readfile(name)
    let opts = {'syntax':'help', 'color':'QuickPreview', 'close':'button'}
    let opts.title = 'Help: ' . fnamemodify(name, ':t')
    let g:quickui#tools#hint = item[1]
    let opts.command = ['silent! exec g:quickui#tools#hint']
    let opts.command += ["exec 'nohl'"]
    let opts.command += ["normal zz"]
    let opts.w = 80
    " echom opts
    let winid = quickui#textbox#open(content, opts)
    return 0
endfunc



"----------------------------------------------------------------------
" save curses help
"----------------------------------------------------------------------
let s:previous_cursor = {}

function! s:remember_cursor_context(code)
    let hwnd = g:quickui#context#current
    let name = hwnd.opts.keep_name
    let s:previous_cursor[name] = g:quickui#context#cursor
endfunc

function! s:remember_cursor_listbox(code)
    let hwnd = g:quickui#listbox#current
    let name = hwnd.opts.keep_name
    let s:previous_cursor[name] = g:quickui#listbox#cursor
endfunc

function! quickui#tools#clever_context(name, content, opts)
    let opts = deepcopy(a:opts)
    let opts.index = get(s:previous_cursor, a:name, -1)
    let opts.keep_name = a:name
    let opts.callback = function('s:remember_cursor_context')
    let content = quickui#context#reduce_items(a:content)
    call quickui#context#open_nested(content, opts)
endfunc

function! quickui#tools#clever_listbox(name, content, opts)
    let opts = deepcopy(a:opts)
    let opts.index = get(s:previous_cursor, a:name, -1)
    let opts.keep_name = a:name
    let opts.callback = function('s:remember_cursor_listbox')
    call quickui#listbox#open(a:content, opts)
endfunc


"----------------------------------------------------------------------
" terminal
"----------------------------------------------------------------------
function! quickui#tools#terminal(name)
    if !exists('g:quickui_terminal_tools')
        let g:quickui_terminal_tools = {}
    endif
    if !has_key(g:quickui_terminal_tools, a:name)
        call quickui#utils#errmsg('ERROR: tool ' . a:name . ' not defined !')
        return -1
    endif
    let tools = g:quickui_terminal_tools[a:name]
    if !has_key(tools, 'cmd')
        call quickui#utils#errmsg('ERROR: key cmd not present in tool ' . a:name)
        return -1
    endif
    let opts = {}
    let cmd = tools.cmd
    let w = get(g:, 'quickui_terminal_w', 80)
    let h = get(g:, 'quickui_terminal_h', 24)
    let opts.w = get(tools, 'w', w)
    let opts.h = get(tools, 'h', h)
    if has_key(tools, 'color')
        if tools.color != ''
            let opts.color = tools.color
        endif
    endif
    if has_key(tools, 'title')
        let opts.title = tools.title
    endif
    if has_key(tools, 'callback')
        let opts.callback = tools.callback
    endif
    if has_key(tools, 'prepare')
        let opts.prepare = tools.prepare
    endif
    if has_key(tools, 'cwd')
        let opts.cwd = tools.cwd
    endif
    if has_key(tools, 'script')
        let opts.safe = tools.script
    endif
    if has_key(tools, 'pause')
        if tools.pause
            let opts.pause = 1
        endif
    endif
    if has_key(tools, 'close')
        if tools.close
            let opts.close = 1
        endif
    endif
    call quickui#terminal#dialog(cmd, opts)
    return 0
endfunc


"----------------------------------------------------------------------
" search inputbox
"----------------------------------------------------------------------
function! quickui#tools#input_search()
    let cword = expand('<cword>')
    let title = 'Enter text to search:'
    let text = quickui#input#open(title, cword, 'search')
    redraw
    if text != ''
        let text = escape(text, '[\/*~^.')
        call feedkeys("\<ESC>/" . text . "\<cr>", 'n')
    endif
endfunc



